package service

import (
	"fmt"
	"github.com/kordar/k-means-server/src/pojo"
	"github.com/kordar/k-means-server/src/util"
	"log"
)

type KMeansPlus struct {
	Data       []pojo.FeatureItem
	FeatureMap map[string][]float64
	Len        int
}

func (k KMeansPlus) getCenter(centers []pojo.FeatureItem) pojo.FeatureItem {
	// 所有数据点与中心点比较，获取最小相似度值作为新的中心点
	centerMap := make(map[int]*pojo.FeatureItem)
	for _, center := range centers {
		centerMap[center.Id] = &center
	}

	var minItem pojo.FeatureItem
	min := 1.0
	for _, item := range k.Data {
		if centerMap[item.Id] != nil {
			continue
		}
		for _, center := range centers {
			cosine := util.Cosine(k.FeatureMap[item.Url], k.FeatureMap[center.Url])
			if cosine < min {
				min = cosine
				minItem = item
			}
		}
	}
	return minItem
}

func (k KMeansPlus) Run() map[pojo.FeatureItem][]pojo.FeatureItem {
	// 1、随机获取一个点作为初始聚类中心
	centers := make([]pojo.FeatureItem, 0)
	centers = append(centers, k.Data[0])
	groups := make(map[pojo.FeatureItem][]pojo.FeatureItem)
	groups[k.Data[0]] = make([]pojo.FeatureItem, 0)
	for true {
		l := len(centers)
		if l >= k.Len || l >= len(k.Data) {
			break
		}
		center := k.getCenter(centers)
		centers = append(centers, center)
		groups[center] = make([]pojo.FeatureItem, 0)
	}

	return k.cluster(centers, groups)
}

func (k KMeansPlus) cluster(centers []pojo.FeatureItem, groups map[pojo.FeatureItem][]pojo.FeatureItem) map[pojo.FeatureItem][]pojo.FeatureItem {
	log.Println(fmt.Sprintf("********centers=%d*******groups=%d****************", len(centers), len(groups)))
	// 计算样本到中心点的距离
	for _, datum := range k.Data {
		compare := util.GetMaxCompare(centers, datum, k.FeatureMap)
		groups[compare] = append(groups[compare], datum)
	}

	isGoon := false
	newCenters := make([]pojo.FeatureItem, 0)
	newGroups := make(map[pojo.FeatureItem][]pojo.FeatureItem)
	for key, group := range groups {
		// 新的中心点
		gg := util.GetCenterByCompareSum(group, k.FeatureMap)
		newCenters = append(newCenters, gg) // 新的中心点
		newGroups[gg] = make([]pojo.FeatureItem, 0)
		if gg.Id != key.Id {
			isGoon = true
		}
	}

	if isGoon {
		return k.cluster(newCenters, newGroups)
	}

	return groups

}
